﻿using Newtonsoft.Json;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.MP.AdvancedAPIs.OAuth;
using Senparc.Weixin.MP.AdvancedAPIs.User;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.WebSockets;
using ZBJF.Warranty.BLL;
using ZBJF.Warranty.Common.Common;
using ZBJF.Warranty.Common.Enums.SocketEnum;
using ZBJF.Warranty.Model;
using ZBJF.Warranty.Model.ViewModel.SocketModel;
using ZBJF.Warranty.WeiXin;

namespace ZBJF.Warranty.CustomerService.Controllers
{
    public class SocketApiController : ApiController
    {
        #region 连接池
        private static object objlock = new object(); //线程锁
        /// <summary>
        /// 客服Socket连接池
        /// </summary>
        private static Dictionary<string, WebSocket> CONNECT_POOL = new Dictionary<string, WebSocket>();
        /// <summary>
        /// 离线消息池
        /// </summary>
        private static Dictionary<string, List<MessageInfo>> MESSAGE_POOL = new Dictionary<string, List<MessageInfo>>();

        /// <summary>
        /// 用户与客服的连接通道 <客户OpenId,客服UserId>
        /// </summary>
        private static Dictionary<string, string> Customer_Conn_Service = new Dictionary<string, string>();
        /// <summary>
        /// 客服当前的连接数量 <客服UserId,连接数量 >
        /// </summary>
       // private static Dictionary<string, int> CustomerServerConnCount = new Dictionary<string, int>();

        /// <summary>
        /// 客户连接池 <客户OpenId,客服消息集合>
        /// </summary>
        private static Dictionary<string, CustomerBaseInfo> CustomerConnPool = new Dictionary<string, CustomerBaseInfo>();

        // private static Dictionary<string, DateTime> SocketLast = new Dictionary<string, DateTime>();//心跳包 用于判断后台是否断开

        #endregion

        public HttpResponseMessage Get()
        {
            if (HttpContext.Current.IsWebSocketRequest)
            {
                HttpContext.Current.AcceptWebSocketRequest(ProcessChat);
            }
            return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
        }

        private async Task ProcessChat(AspNetWebSocketContext context)
        {
            WebSocket socket = context.WebSocket;
            string user = context.QueryString["user"].ToString();

            try
            {
                #region 用户添加连接池
                //第一次open时，添加到连接池中
                if (!CONNECT_POOL.ContainsKey(user))
                {
                    //推送给所有客服，在线客服数量+1
                    SocketMessageInfoModel<int> mess = new SocketMessageInfoModel<int>(ResponseMessageTypeEnum.OnlineService, 1);
                    SendMessageToService<int>(mess);

                    CONNECT_POOL.Add(user, socket);//不存在，添加
                    //CustomerServerConnCount.Add(user, 0);
                }
                else if (socket != CONNECT_POOL[user])//当前对象不一致，更新
                    CONNECT_POOL[user] = socket;
                #endregion

                #region 离线消息处理
                if (MESSAGE_POOL.ContainsKey(user))
                {
                    List<MessageInfo> msgs = MESSAGE_POOL[user];
                    foreach (MessageInfo item in msgs)
                    {
                        socket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(item.MsgTime + ":" + item.MsgContent)), WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    MESSAGE_POOL.Remove(user);//移除离线消息
                }
                #endregion

                string descUser = string.Empty;//目的用户
                while (true)
                {
                    if (socket.State == WebSocketState.Open)
                    {
                        ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
                        WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);

                        #region 消息处理（字符截取、消息转发）
                        try
                        {
                            #region 关闭Socket处理，删除连接池
                            if (socket.State != WebSocketState.Open)//连接关闭
                            {
                                if (CONNECT_POOL.ContainsKey(user))
                                    CONNECT_POOL.Remove(user);//删除连接池
                                //if (CustomerServerConnCount.ContainsKey(user))
                                //    CustomerServerConnCount.Remove(user);//连接数量池
                                //清除链接通道
                                List<string> list = new List<string>();
                                foreach (KeyValuePair<string, string> kvp in Customer_Conn_Service)
                                {
                                    if (kvp.Value.Equals(user))
                                    {
                                        list.Add(kvp.Key);
                                    }
                                }
                                foreach (string item in list)
                                {
                                    if(Customer_Conn_Service.ContainsKey(item))
                                        Customer_Conn_Service.Remove(item);
                                }
                                SocketMessageInfoModel<int> mess = new SocketMessageInfoModel<int>(ResponseMessageTypeEnum.OfflineService, 1);
                                SendMessageToService<int>(mess);
                                LogHelper.WriteLog("WebSocket关闭:UserId:" + user, LogType.remark);
                                break;
                            }
                            #endregion

                            string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);//发送过来的消息
                            string[] msgList = userMsg.Split('|');
                            if (msgList.Length == 2)
                            {
                                if (msgList[0].Trim().Length > 0)
                                    descUser = msgList[0].Trim();//记录消息目的用户
                                buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msgList[1]));

                                if (CONNECT_POOL.ContainsKey(descUser))//判断客户端是否在线
                                {
                                    WebSocket destSocket = CONNECT_POOL[descUser];//目的客户端---发送消息
                                    if (destSocket != null && destSocket.State == WebSocketState.Open)
                                        await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                                }
                                else
                                {
                                    Task.Run(() =>
                                    {
                                        if (!MESSAGE_POOL.ContainsKey(descUser))//将用户添加至离线消息池中
                                            MESSAGE_POOL.Add(descUser, new List<MessageInfo>());
                                        MESSAGE_POOL[descUser].Add(new MessageInfo(DateTime.Now, buffer));//添加离线消息
                                    });
                                }

                            }
                            else
                            {
                                buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMsg));

                                foreach (KeyValuePair<string, WebSocket> item in CONNECT_POOL)
                                {
                                    await item.Value.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            LogHelper.WriteLog("socket通讯消息处理异常：" + ex.ToString(), LogType.error);
                        }
                        #endregion
                    }
                    else
                    {
                        break;
                    }
                }//while end
            }
            catch (Exception ex)
            {
                ////整体异常处理
                LogHelper.WriteLog("socket通讯整体异常：" + ex.ToString(), LogType.error);
            }
        }

        #region 获取当前的所有待接入用户 + ResultModel GetAllCustomerWaitGet()
        /// <summary>
        /// 获取当前的所有待接入用户
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ResultModel GetAllCustomerWaitGet()
        {
            var result = CustomerConnPool.Values.ToList();
            return new ResultModel(true, "获取成功", CodeStateEnum.Success, result);
        }
        #endregion

        #region 客服接入客户 + ResultModel CustomerServiceGet(string openID)
        /// <summary>
        /// 客服接入客户
        /// </summary>
        /// <param name="openID">openID</param>
        /// <returns></returns>
        [HttpGet]
        public ResultModel CustomerServiceGet(string openID, string userId)
        {
            lock (objlock)
            {
                if (CustomerConnPool.ContainsKey(openID))
                {
                    CustomerBaseInfo result = CustomerConnPool[openID];
                    //从待接入列表中移除他
                    CustomerConnPool.Remove(openID);
                    //创建用户与客服的连接池
                    if (!Customer_Conn_Service.ContainsKey(openID))
                        Customer_Conn_Service.Add(openID, userId);
                    else
                        Customer_Conn_Service[openID] = userId;

                    //通知其他客服，把他们的待接入列表中的此客户去掉
                    if (CONNECT_POOL.Count > 0)
                    {
                        SocketMessageInfoModel<string> mess = new SocketMessageInfoModel<string>(ResponseMessageTypeEnum.RemoveCustomer, openID);
                        SendMessageToService<string>(mess);
                    }
                    return new ResultModel(true, "获取成功", CodeStateEnum.Success, result);
                }
                else
                {
                    LogHelper.WriteLog("客服接入客户，但客户不存在了：openID：" + openID + ";userId:" + userId, LogType.remark);
                    return new ResultModel(false, "不存在此客户", CodeStateEnum.Failed);
                }
            }
        }
        #endregion

        #region 客服关闭用户连接 + ResultModel CustomerCloseConn(string openId, string userId)
        /// <summary>
        /// 客服关闭用户连接
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ResultModel CustomerCloseConn(string openId, bool isSendMessage)
        {
            if (Customer_Conn_Service.ContainsKey(openId))
            {
                Customer_Conn_Service.Remove(openId);
            }
            //通知用户本次通话结束
            if (isSendMessage)
                WeiXinHelper.SendCustomerServiceMessage_Text(openId, "本次通话已结束！", WeiXinHelper.GetH5AccessToken());
            return new ResultModel(true, "处理成功", CodeStateEnum.Success);
        }
        #endregion

        #region 第三方客户消息接口 + async Task CustomerMassage(string message, string openId)
        /// <summary>
        /// 第三方客户消息接口
        /// </summary>
        /// <param name="message">消息内容</param>
        /// <param name="openId">微信公众号的OpenId</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ResultModel> CustomerMassage(CustomerPoolModel model)
        {
            LogHelper.WriteLog("收到微信客户消息：" + JsonConvert.SerializeObject(model), LogType.remark);
            //图片消息
            if (model.MessageType == MessageTypeEnum.Image)
            {
                string path = string.Format("/UploadFile/File/workOrder/{0}/{1}/{2}/{3}/", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, model.OpenId);
                //把网络图片保存到本地
                model.Message = FileHelper.SaveNetworkImage(model.Message, path, System.Guid.NewGuid() + ".png");
            }//语音消息
            else if (model.MessageType == MessageTypeEnum.Voice)
            {
                string path = string.Format("/UploadFile/File/Voice/{0}/{1}/{2}/{3}/", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, model.OpenId);
                string fullPath = System.Web.HttpContext.Current.Server.MapPath(path);
                string fileName = DateTime.Now.ToString("hhmmssff") + "." + model.FileType;

                //把语音消息保存到本地
                bool isOk = WeiXinHelper.LoadVoiceFile(WeiXinHelper.GetH5AccessToken(), model.Message, fullPath, fileName);
                if (isOk)
                {
                    //把文件格式转换成 .mp3
                    FfmpegFileChangHelper helper = new FfmpegFileChangHelper();
                    string newFileName = FileHelper.ChangeFileExten(fullPath + fileName, ".mp3");
                    bool changOk = helper.VoiceFileFormatChang(fullPath + fileName, newFileName);
                    //转换成功
                    if (changOk)
                    {
                        model.Message = FileHelper.ChangeFileExten(path + fileName, ".mp3");
                    }//转换失败
                    else
                    {
                        LogHelper.WriteLog("转换语音文件失败", LogType.remark);
                        model.Message = path + fileName;
                    }
                }
                else
                {
                    LogHelper.WriteLog("保存语音文件失败", LogType.remark);
                }
            }
            string toUser = "2"; //必须确保系统用户表(systemUer)里有这个ID
            if (Customer_Conn_Service.ContainsKey(model.OpenId))
            {
                #region 已经存在通话
                toUser = Customer_Conn_Service[model.OpenId];
                SocketMessageInfoModel<CustomerPoolModel> mess = new SocketMessageInfoModel<CustomerPoolModel>(ResponseMessageTypeEnum.Normal, model);
                ResultModel result = SendMessageToService<CustomerPoolModel>(mess, toUser);
                //客服掉线
                if (result.CodeState == CodeStateEnum.ServiceOffline)
                {
                    WeiXinHelper.SendCustomerServiceMessage_Text(model.OpenId, "当前客服已掉线，将会有其她客服为您服务。", WeiXinHelper.GetH5AccessToken());
                    AddNewCustomer(model);
                    Customer_Conn_Service.Remove(model.OpenId);
                }
                //把消息保存到数据库
                ServiceFactory.WeiXinChatRecordServiceInstance.AddEntities(new WeiXinChatRecord()
                {
                    Message = model.Message,
                    MessageSource = (short)WeiXinChatRecordMessageSourceEnum.Customer,
                    OpenId = model.OpenId,
                    MessageType = (short)model.MessageType,
                    CreateTime = model.Time,
                    CustomerServiceId = int.Parse(toUser)
                });
                return result;
                #endregion
            }
            else
            {
                //把消息保存到数据库
                ServiceFactory.WeiXinChatRecordServiceInstance.AddEntities(new WeiXinChatRecord()
                {
                    Message = model.Message,
                    MessageSource = (short)WeiXinChatRecordMessageSourceEnum.Customer,
                    OpenId = model.OpenId,
                    MessageType = (short)model.MessageType,
                    CreateTime = model.Time,
                    CustomerServiceId = int.Parse(toUser)
                });
                #region 不存在通话信息
                //没有客服在线
                if (CONNECT_POOL.Count <= 0)
                {
                    WeiXinHelper.SendCustomerServiceMessage_Text(model.OpenId, "当前没有客服在线，请稍后再试", WeiXinHelper.GetH5AccessToken());
                    return new ResultModel(true, "当前没有客服在线。", CodeStateEnum.NoOnlineService);
                }

                //用户连接池是否已经创建，一般用户第二次发消息的时候就已经是创建了的
                if (CustomerConnPool.ContainsKey(model.OpenId))
                {
                    CustomerConnPool[model.OpenId].CustomerPoolModel.Add(model);
                    return new ResultModel(true, "请求成功。", CodeStateEnum.Success);
                }
                else
                {
                    AddNewCustomer(model);
                    return new ResultModel(true, "请求成功。", CodeStateEnum.Success);
                }
                #endregion
            }


        }
        #endregion

        #region 获取当前在线客服数量 + ResultModel GetCurrentOnlineServiceCount()
        [HttpPost]
        public ResultModel GetCurrentOnlineServiceCount()
        {
            return new ResultModel(true, "获取成功", CodeStateEnum.Success, CONNECT_POOL.Count());
        }
        #endregion

        #region 推送消息给客服 + void SendMessageToService<T>(SocketMessageInfoModel<T> mess,string userId,string openId)
        private ResultModel SendMessageToService<T>(SocketMessageInfoModel<T> mess, string userId = "", string openId = "")
        {
            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
            buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(mess)));
            //存在客服用户ID就发给他一个人
            if (!string.IsNullOrEmpty(userId))
            {
                //判断客服是否在线
                if (CONNECT_POOL.ContainsKey(userId))
                {
                    WebSocket destSocket = CONNECT_POOL[userId];//目的客户端
                    try
                    {
                        if (destSocket != null && destSocket.State == WebSocketState.Open)
                            destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                        return new ResultModel(true, "发送成功", CodeStateEnum.Success);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteLog("发送消息给客服失败：客服ID:" + userId + "；异常信息：" + ex.ToString(), LogType.remark);
                        CONNECT_POOL.Remove(userId);//删除连接池
                    }
                    return new ResultModel(false, "发送失败", CodeStateEnum.Failed);
                }
                else
                    return new ResultModel(true, "客服不在线", CodeStateEnum.ServiceOffline);
            }

            //不存在客服用户ID就发给所有客服
            if (CONNECT_POOL.Count() > 0)
            {
                foreach (var item in CONNECT_POOL)
                {
                    try
                    {

                        if (item.Value != null && item.Value.State == WebSocketState.Open)
                            item.Value.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteLog("发送消息给客服失败：客服ID:" + item.Key + "；异常信息：" + ex.ToString(), LogType.remark);
                        CONNECT_POOL.Remove(item.Key);//删除连接池
                    }
                }
            }
            return new ResultModel(true, "发送成功", CodeStateEnum.Success);
        }
        #endregion

        #region 新增用户连接池 + void AddNewCustomer(CustomerPoolModel model)
        public void AddNewCustomer(CustomerPoolModel model)
        {
            CustomerBaseInfo info = new CustomerBaseInfo();
            info.OpenId = model.OpenId;
            info.CustomerPoolModel = new List<CustomerPoolModel>() { model };
            #region 获取客户昵称和头像Url
            WeiXinUserInfo userInfo = ServiceFactory.WeiXinUserInfoServiceInstance.LoadEntities(t => t.OpenId == model.OpenId).FirstOrDefault();
            //1、调用微信接口
            UserInfoJson user = WeiXinHelper.GetUserInfoBase(model.OpenId, WeiXinHelper.GetH5AccessToken());
            if (user != null)
            {
                info.NickName = user.nickname;
                info.HeadLogo = user.headimgurl;
            }
            else
            {    //使用以前的信息
                if (userInfo != null && !string.IsNullOrEmpty(userInfo.OpenId)) //更新
                {
                    info.NickName = userInfo.NickName;
                    info.HeadLogo = userInfo.HeadLogo;
                }
                else
                {
                    info.NickName = "微信客户";
                    info.HeadLogo = "/Images/3.jpg";
                }
            }

            //2、保存到数据库
            if (userInfo != null && !string.IsNullOrEmpty(userInfo.OpenId)) //更新
            {
                userInfo.HeadLogo = info.HeadLogo;
                userInfo.NickName = info.NickName;
                info.RealName = userInfo.RealName;
                info.Remark = userInfo.Remark;
                ServiceFactory.WeiXinUserInfoServiceInstance.UpdateEntities(userInfo);
            }
            else
            {
                var addUser = ServiceFactory.WeiXinUserInfoServiceInstance.AddEntities(new WeiXinUserInfo //新增
                {
                    OpenId = model.OpenId,
                    HeadLogo = info.HeadLogo,
                    NickName = info.NickName,
                    CreateTime = DateTime.Now
                });

            }
            #endregion

            CustomerConnPool.Add(model.OpenId, info);
            //把新客户推送给客服
            SocketMessageInfoModel<List<CustomerBaseInfo>> mess = new SocketMessageInfoModel<List<CustomerBaseInfo>>(ResponseMessageTypeEnum.AddCustomer, new List<CustomerBaseInfo> { info });
            SendMessageToService<List<CustomerBaseInfo>>(mess);
        }
        #endregion

        #region 接入最近会话用户 + ResultModel CurrentCustomerState(string openId,string userId)
        [HttpGet]
        public ResultModel CurrentCustomerState(string openId, string userId)
        {
            if (string.IsNullOrEmpty(openId))
                return new ResultModel(false, "参数错误！", CodeStateEnum.ParamError);

            if (Customer_Conn_Service.ContainsKey(openId))
            {
                if (Customer_Conn_Service[openId] == userId)
                {
                    return new ResultModel(true, "接入成功！", CodeStateEnum.Success);
                }
                else
                    return new ResultModel(false, "用户正在与客服通话中！", CodeStateEnum.Failed);
            }

            if (CustomerConnPool.ContainsKey(openId))
                return new ResultModel(false, "用户正在待接入列表中！", CodeStateEnum.Failed);

            Customer_Conn_Service.Add(openId, userId);
            return new ResultModel(true, "接入成功！", CodeStateEnum.Success);
        }
        #endregion
    }
}